import { Injectable } from '@nestjs/common';
import { BadRequestException } from '@nestjs/common';

export interface ParamValidationRule {
  type: 'string' | 'number' | 'boolean' | 'array' | 'object';
  required?: boolean;
  min?: number;
  max?: number;
  pattern?: string;
  enum?: any[];
  items?: ParamValidationRule; // 用于数组类型的元素验证
  properties?: Record<string, ParamValidationRule>; // 用于对象类型的属性验证
}

@Injectable()
export class ParamValidatorService {
  // 验证工作流参数
  async validateWorkflowParams(
    params: Record<string, any>,
    schema: Record<string, ParamValidationRule>
  ): Promise<void> {
    const errors: string[] = [];

    for (const [key, rule] of Object.entries(schema)) {
      try {
        await this.validateParam(params[key], rule, key);
      } catch (error) {
        errors.push(error.message);
      }
    }

    if (errors.length > 0) {
      throw new BadRequestException(errors);
    }
  }

  // 验证单个参数
  private async validateParam(
    value: any,
    rule: ParamValidationRule,
    path: string
  ): Promise<void> {
    // 检查必填
    if (rule.required && (value === undefined || value === null)) {
      throw new Error(`${path} is required`);
    }

    // 如果值为空且非必填，则跳过后续验证
    if (value === undefined || value === null) {
      return;
    }

    // 类型检查
    switch (rule.type) {
      case 'string':
        this.validateString(value, rule, path);
        break;
      case 'number':
        this.validateNumber(value, rule, path);
        break;
      case 'boolean':
        this.validateBoolean(value, path);
        break;
      case 'array':
        await this.validateArray(value, rule, path);
        break;
      case 'object':
        await this.validateObject(value, rule, path);
        break;
      default:
        throw new Error(`${path}: Unsupported type ${rule.type}`);
    }
  }

  // 验证字符串
  private validateString(value: any, rule: ParamValidationRule, path: string): void {
    if (typeof value !== 'string') {
      throw new Error(`${path} must be a string`);
    }

    if (rule.min !== undefined && value.length < rule.min) {
      throw new Error(`${path} length must be at least ${rule.min}`);
    }

    if (rule.max !== undefined && value.length > rule.max) {
      throw new Error(`${path} length must be at most ${rule.max}`);
    }

    if (rule.pattern && !new RegExp(rule.pattern).test(value)) {
      throw new Error(`${path} does not match required pattern`);
    }

    if (rule.enum && !rule.enum.includes(value)) {
      throw new Error(`${path} must be one of: ${rule.enum.join(', ')}`);
    }
  }

  // 验证数字
  private validateNumber(value: any, rule: ParamValidationRule, path: string): void {
    if (typeof value !== 'number') {
      throw new Error(`${path} must be a number`);
    }

    if (rule.min !== undefined && value < rule.min) {
      throw new Error(`${path} must be at least ${rule.min}`);
    }

    if (rule.max !== undefined && value > rule.max) {
      throw new Error(`${path} must be at most ${rule.max}`);
    }
  }

  // 验证布尔值
  private validateBoolean(value: any, path: string): void {
    if (typeof value !== 'boolean') {
      throw new Error(`${path} must be a boolean`);
    }
  }

  // 验证数组
  private async validateArray(
    value: any,
    rule: ParamValidationRule,
    path: string
  ): Promise<void> {
    if (!Array.isArray(value)) {
      throw new Error(`${path} must be an array`);
    }

    if (rule.min !== undefined && value.length < rule.min) {
      throw new Error(`${path} must contain at least ${rule.min} items`);
    }

    if (rule.max !== undefined && value.length > rule.max) {
      throw new Error(`${path} must contain at most ${rule.max} items`);
    }

    if (rule.items) {
      for (let i = 0; i < value.length; i++) {
        await this.validateParam(value[i], rule.items, `${path}[${i}]`);
      }
    }
  }

  // 验证对象
  private async validateObject(
    value: any,
    rule: ParamValidationRule,
    path: string
  ): Promise<void> {
    if (typeof value !== 'object' || Array.isArray(value)) {
      throw new Error(`${path} must be an object`);
    }

    if (rule.properties) {
      for (const [key, propertyRule] of Object.entries(rule.properties)) {
        await this.validateParam(value[key], propertyRule, `${path}.${key}`);
      }
    }
  }
} 